package com.turman.common.net

import androidx.collection.SimpleArrayMap
import com.google.gson.Gson
import com.turman.common.net.config.CniaoInterceptor
import com.turman.common.net.config.KtHttpLogInterceptor
import com.turman.common.net.config.LocalCookieJar
import com.turman.common.net.config.RetryInterceptor
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resumeWithException

class OkHttpApi private constructor(): HttpApi {
    var baseURL:String = ""   //host地址
    var maxRetry = 0 //最大重试次数

    //用于取消请求操作
    private val callMap = SimpleArrayMap<Any, Call>()

    private val defaultClient:OkHttpClient = OkHttpClient.Builder()
        .callTimeout(10, TimeUnit.SECONDS)
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .retryOnConnectionFailure(true)
        .followRedirects(false)  //不支持重定向
        .cache(Cache(File("sdcard/cache","okhttp"),1024))  //cache设置
//        .cookieJar(CookieJar.NO_COOKIES)
        .cookieJar(LocalCookieJar())  //cookie缓存策略
        .addNetworkInterceptor(CniaoInterceptor())
        .addNetworkInterceptor(KtHttpLogInterceptor{
            logLevel(KtHttpLogInterceptor.LogLevel.BODY)
        })
        .addNetworkInterceptor(RetryInterceptor(maxRetry))
        .build()

    //这个变量方便自定义client设置
    private var mClient:OkHttpClient = defaultClient
    fun getClient() = mClient

    fun initConfig(client:OkHttpClient){
        this.mClient = client
    }

    //单例
    companion object {
        @Volatile
        private var api: OkHttpApi? = null

        @Synchronized
        fun getInstance(): OkHttpApi {
            return api ?: OkHttpApi().also { api =it }
        }
    }

    override fun get(params: Map<String, Any>, path: String, callback: IHttpCallback) {
        val urlBuilder = "$baseURL$path".toHttpUrl().newBuilder()
        params.forEach{
            urlBuilder.addEncodedQueryParameter(it.key,it.value.toString())
        }
        val request: Request = Request.Builder()
            .get()
            .tag(params) //添加tag 用于取消请求
            .url(urlBuilder.build())
            .cacheControl(CacheControl.FORCE_NETWORK)
            .build()

        mClient.newCall(request).let { callMap.put(params,it) }?.enqueue(object: Callback {
            override fun onFailure(call: Call, e: IOException) {
                callback.onFailed(e.message)
            }

            override fun onResponse(call: Call, response: Response) {
                callback.onSuccess(response)
            }
        })
    }

    override fun getSync(params: Map<String, Any>, path: String): Any? {
        val urlBuilder = "$baseURL$path".toHttpUrl().newBuilder()
        params.forEach{
            urlBuilder.addEncodedQueryParameter(it.key,it.value.toString())
        }
        val request: Request = Request.Builder()
            .get()
            .tag(params)
            .url(urlBuilder.build())
            .cacheControl(CacheControl.FORCE_NETWORK)
            .build()

        return mClient.newCall(request).let { callMap.put(params,it) }?.execute()
    }

    override fun post(body: Any, path: String, callback: IHttpCallback) {
        val url = "$baseURL$path"
        val request: Request = Request.Builder()
            .post(Gson().toJson(body).toRequestBody())
            .tag(body)
            .url(url)
            .build()

        mClient.newCall(request).let { callMap.put(body,it) }?.enqueue(object: Callback {
            override fun onFailure(call: Call, e: IOException) {
                callback.onFailed(e.message)
            }

            override fun onResponse(call: Call, response: Response) {
                callback.onSuccess(response)
            }
        })
    }

    override fun postSync(body: Any, path: String): Any? {
        val url = "$baseURL$path"
        val request: Request = Request.Builder()
            .post(Gson().toJson(body).toRequestBody())
            .tag(body)
            .url(url)
            .build()

        return mClient.newCall(request).let { callMap.put(body,it) }?.execute()
    }

    //支持协程的调用,注意看 这里就不需要回调参数了
    fun get(params:Map<String,Any>,path:String) = runBlocking {
        val urlBuilder = "$baseURL$path".toHttpUrl().newBuilder()
        params.forEach{
            urlBuilder.addEncodedQueryParameter(it.key,it.value.toString())
        }
        val request: Request = Request.Builder()
            .get()
            .tag(params) //添加tag 用于取消请求
            .url(urlBuilder.build())
            .cacheControl(CacheControl.FORCE_NETWORK)
            .build()

        mClient.newCall(request).let { callMap.put(params,it) }?.call()
    }

    //扩展Call的异步执行
    private suspend fun Call.call(async:Boolean=true):Response {
        return suspendCancellableCoroutine {
            if (async) {
                enqueue(object:Callback{
                    override fun onFailure(call: Call, e: IOException) {
                        //避免冗余调用
                        if (it.isCancelled) return
                        it.resumeWithException(e)
                    }

                    override fun onResponse(call: Call, response: Response) {
                        it.resume(response) { cause -> cause.printStackTrace() }
                    }
                })
            } else {
                it.resume(execute()){ cause -> cause.printStackTrace() }
            }
            it.invokeOnCancellation {
                try {
                    cancel()
                } catch (ex:Exception) {
                    ex.printStackTrace()
                }
            }
        }
    }

    override fun cancelRequest(tag: Any) {
        callMap.get(tag)?.cancel()
    }

    override fun cancelAllRequest() {
        for (i:Int in 0 until callMap.size()) {
            callMap.get(callMap.keyAt(i))?.cancel()
        }
    }
}